package com.mini.mysql.service;

import cn.hutool.crypto.symmetric.AES;
import com.mini.common.EncryptField;
import com.mini.util.AsyncTaskManager;
import com.mini.util.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Session;
import org.hibernate.jpa.HibernateEntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.JedisCommands;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Transient;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.util.*;

/**
 * @author zhangxiaobin
 * @title: BaseService
 * @description:
 * @date 2019/10/2711:35
 */
@Slf4j
public class BaseService {

    @Autowired
    AsyncTaskManager asyncTaskManager;

    @Autowired
    RedisTemplate redisTemplate;

    @Value("${mail.notification.env}")
    private String notificationEnv;

    @Value("${mail.notification.url}")
    private String notificationUrl;

    @Value("${mail.notification.users}")
    private String notificationUsers;

    @Autowired
    private AES aes;

    @PersistenceContext
    EntityManager entityManager;


    //构建PageRequest
    protected PageRequest buildPageRequest(int pageNumber, int pagzSize, Sort sort) {
        return new PageRequest(pageNumber - 1, pagzSize, sort);
    }

    public void sendEmail(String message){
        if("test".equalsIgnoreCase(notificationEnv)){
            return;
        }
        try{
            asyncTaskManager.getExecutor().submit(new Runnable() {
                @Override
                public void run() {
                    Map<String, Object> requestEntity = new HashMap<>();
                    String ipAddress = "127.0.0.1";
                    try {
                        ipAddress = InetAddress.getLocalHost().getHostAddress();
                    }catch ( Exception ex){
                        log.error("sendMailNotification get ip error , ex={}", ex);
                    }
                    requestEntity.put("env", "mini-data-es-"+notificationEnv);
                    requestEntity.put("sys", "mini-data-es");
                    requestEntity.put("title", "报警服务IP【"+ipAddress+"】");
                    requestEntity.put("emailAddr", notificationUsers);
                    requestEntity.put("ip", ipAddress);
                    requestEntity.put("msg", message);
                    boolean successState = true;
                    int httpCount = 0 ;
                    do {
                        try {
                            HttpClientUtil.post(notificationUrl,requestEntity);
                            successState = true;
                        } catch (IOException e) {
                            log.error("send email error, msg = {}, {}",message,e);
                            successState = false;
                            httpCount++;
                        }
                    }while (!successState && httpCount < 5);
                }
            });
        }catch (Exception ex){
            log.error("send email error message = {} ,ex= {}",message, ex);
        }
    }

    protected boolean tryLock(String key){
        try {
            String result = (String) redisTemplate.execute((RedisCallback<String>) connection -> {
                JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                return commands.set(key, key, "NX", "EX", 5);
            });
            return !StringUtils.isEmpty(result);
        } catch (Exception e) {
            log.error("getNewUsePushLock error", e);
        }
        return false;
    }

    protected void releaseLockKey(String key){
        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("releaseNewUsePushLock error", e);
        }
    }

    public String encryptField(String val) {
        if (StringUtils.isBlank(val)) {
            return null;
        }
        boolean need = false;
        try {
            aes.decrypt(val);
        } catch (RuntimeException r) {
            need = true;
        }
        if (!need) {
            return val;
        }
        return aes.encryptHex(val);
    }

    public String decryptField(String val) {
        if (StringUtils.isBlank(val)) {
            return null;
        }
        try {
            return aes.decryptStr(val);
        } catch (RuntimeException r) {
        }
        return val;
    }

    public Object encryptObj(Object obj) {
        if(obj==null){
            return null;
        }
        try {
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if(Objects.nonNull(field.getAnnotation(Transient.class))){
                    evictObj(field.get(obj));
                }
                if (Objects.isNull(field.getAnnotation(EncryptField.class))) {
                    field.setAccessible(false);
                    continue;
                }
                String val = encryptField((String) field.get(obj));
                field.set(obj, val);
                field.setAccessible(false);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return obj;
    }

    public void evictObj(Object obj){
        try {
            Session session = ((HibernateEntityManager) entityManager).getSession();
            session.evict(obj);
        }catch (Exception e){
        }
    }


    public Object decryptObj(Object obj) {
        if(obj==null){
            return null;
        }
        try {
            evictObj(obj);
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if (Objects.isNull(field.getAnnotation(EncryptField.class))) {
                    field.setAccessible(false);
                    continue;
                }
                String val = decryptField((String) field.get(obj));
                field.set(obj, val);
                field.setAccessible(false);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return obj;
    }

    public List decryptObjs(List objectList) {
        if(CollectionUtils.isEmpty(objectList)){
            return objectList;
        }
        List result = new ArrayList<>();
        objectList.forEach(o->{
            o = decryptObj(o);
            if(Objects.nonNull(o)){
                result.add(o);
            }

        });
        return result;
    }

}
